// Copyright (c) 1996-2002 John Lyon-Smith All rights reserved.

#pragma once

#include <wxtl\Base.h>
#include <wxtl\ComUnknown.h>

namespace wxtl
{
	template <class T> class ComCreateObject
	{
	public:
		static HRESULT createObject(REFIID riid, void **ppInterface)
		{
			HRESULT hr = S_OK;
			T* pT = new T();
			
			if (pT == NULL)
				return E_OUTOFMEMORY;
			
			hr = pT->QueryInterface(riid, ppInterface);
			
			if (hr == E_NOINTERFACE)
				delete pT;

			hr = pT->ConstructObject();

			return hr;
		}
		
		STDMETHOD(ConstructObject)()
		{
			return S_OK;
		}
	};
	
	class ComClassFactory : 
		public ComUnknown,
		public IClassFactory
	{
	private:
		ComClassFactory() : m_punk(NULL) {}
	
	public:
		ComClassFactory(const CO_CLASS_REGISTER *pCoClass) :
			m_pCoClass(pCoClass), m_punk(NULL) {}
		virtual ~ComClassFactory() {}

	public:
		// IUnknown overrides
		STDMETHOD_(ULONG, AddRef)() { return ComUnknown::AddRef(); }
		STDMETHOD_(ULONG, Release)() { return ComUnknown::Release(); }
		STDMETHOD(QueryInterface)( REFIID riid, void **ppInterface )
		{    
			HRESULT hr = ComUnknown::QueryInterface(riid, ppInterface);
			
			if (SUCCEEDED(hr))
				return hr;
			else if ( riid == IID_IClassFactory )
				*ppInterface = static_cast<IClassFactory *>( this );
			else
			{
				*ppInterface = NULL;                  
				return E_NOINTERFACE;
			}
			
			reinterpret_cast<IUnknown *>(*ppInterface)->AddRef();
			
			return S_OK;
		}
	    
		// IClassFactory 
		STDMETHOD(LockServer)(BOOL fLock )
		{    
			(void)fLock;
			// We are not required to hook any logic since this is always
			// an in-process server, we define the method for completeness
			return S_OK;
		}

		STDMETHOD(CreateInstance)(IUnknown *pUnkOuter, REFIID riid, void **ppInterface)
		{	    
			// This class library does not support aggregation
			if (pUnkOuter != NULL)
				return CLASS_E_NOAGGREGATION;

			// If singleton object
			if (m_pCoClass->bSingleton)
			{
				if (m_punk)
				{
					// Grab the existing one
					m_punk->AddRef();
					*ppInterface = (PVOID)m_punk;
					return S_OK;
				}
				else
				{
					// Create new object and add to singleton map
					HRESULT hr = (*m_pCoClass->pfnCreateObject)(riid, ppInterface);
					
					if (SUCCEEDED(hr))
					{
						m_punk = (IUnknown*)*ppInterface;
					}
					
					return hr;
				}
			}
			else
			{
				// Create new object
				return (*m_pCoClass->pfnCreateObject)(riid, ppInterface);
			}
		}
		
	private:
    	const CO_CLASS_REGISTER *m_pCoClass;		
    	IUnknown* m_punk;	 // Used for singleton objects
	};
}

#pragma warning(default:4244)
